闲话 22.11.25

闲话

完了下面杂题的字号太大了
这段闲话似乎需要在调小下面字号的情况下写(

不知道为什么感觉这边需要变一个画风
?中午还在想写啥 下午就不知道该从何说起了
但一般而言从不知从何说起说起是比较不错的开头
因为我想扯闲天 所以没什么关系

旁边有人问我想干啥
我说我想扯闲天 然后敲完了上面的话

最近感觉上入冬了(
晚上去吃饭的路上裹着羽绒服 紧紧揣着兜
然后看着路灯
总感觉这种感觉之前有过
那时候似乎在下雪
然后就吐槽为什么不下雪
然后被吐槽了白色相簿的季节
是想要的吐槽(

嘛感觉我这边大概上不会有什么心态上的变化
有也不会太大
反正生活都要继续嘛
完了开始沉重了
那就扯开吧

某人看了一点小圆剧场版
然后跟我剧透某人变魔女了
我的回答:肯定是知道内情的人才会出现这种情况,所以是qb(
“你说得对”

昨天为什么没有闲话?
打 mc 打得很嗨
今天为什么有闲话?
不让打 mc 了

感觉上如果缺了闲话会在摘要里补上
嘿打完刚才那句话自动补上了个句号
然后觉得不是很对劲给他删了
就这种写法我是比较喜欢的(
Why so serious?

这种日常感是很重要的呢!
感觉上我最重视的永远是日常感
尤其是在对作品的选择上
小圆这种显然就不在其中了(

似乎写完了
所以下面的标题字号可以调回来了(
草生 没调过来
现在好了 如果有人刚才看到了小小的标题就知道我说的是真的了(

仍然是惯例的杂题!

进行一个闲话的补

[POI2012]PRE-Prefixuffix

对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:

  1. \(L\leq \frac n 2\)
  2. S的L前缀和S的L后缀是循环相同的。

\(1\le n\le 10^6\)

考虑一个串是满足条件的当且仅当其形如 \(\text{ABCBA}\),其中 \(L = \text{AB}\)。容易发现我们需要迅速求出删掉原串两侧左右两侧 border 后(\(\text{BCB}\))的新最长 border 长度。如果这玩意是道子串查询 border 长度的题就没意思了。而且不迅速(

考虑我们需要求的是删除原串两侧定长度子串后剩余串的 border。这玩意可以递推。
\(f_i\) 为原串两侧删除 \(i\) 长度子串后串的 border 长度。初值为 \(f_{\lceil \frac n2\rceil} = 0\) 因为串没了。
每一次向左右两侧拓展一个字符,border 长度最长增加 2。朴素地验证当前长度(\(f_i = f_{i+1} + 2\))是否是一个 border 的长度,不满足就自减 1 后接着查询。
使用哈希做到 \(O(n)\) dp 出每个位置的 border 长度。
随后找到原串所有 border 就做完了。

总时间复杂度 \(O(n)\)

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 1e6 + 10, bse = 131, mod = 1e9 + 3579;
int n, ans, nxt[N], f[N], hsh[N], pw[N];
char ch[N];

int get(int l, int r) { return (1ll * hsh[r] - 1ll * hsh[l - 1] * pw[r - l + 1] % mod + mod) % mod; }

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> ch + 1; pw[0] = 1;
    rep(i,1,n) hsh[i] = (1ll * hsh[i - 1] * bse + ch[i]) % mod, pw[i] = 1ll * pw[i - 1] * bse % mod;
    for (int i = 2, j = 0; i <= n; ++ i) {
        while (j and ch[j + 1] != ch[i]) j = nxt[j];
        if (ch[j + 1] == ch[i]) ++ j;
        nxt[i] = j;
    } 
    int st = (n >> 1) + (n & 1);
    f[st] = 0;
    pre(i, st-1, 0) {
        f[i] = min(f[i + 1] + 2, (n - (i << 1)) >> 1);
        while (f[i] and get(i + 1, i + f[i]) != get(n - i - f[i] + 1, n - i)) -- f[i];
    }
    for (int p = nxt[n]; p; p = nxt[p]) if (p < st) ans = max(ans, p + f[p]);
    cout << ans << '\n';
}



CF961F

给定一个长度为 \(n\) 的字符串 \(T\) 。定义 \(k\) 子串表示 \(S_k , S_{k+1} ~ \cdots S_{n - k +1}\) 。显然的, \(1\) 子串= \(T\),并且有 \(\lceil \frac n 2 \rceil\)\(k\) 子串。

对于每一个 \(k\) 子串 \(k=1,2,3...\lceil \frac n 2 \rceil\) ,试找出最大长度的字符串 \(t\),使得 \(t\)\(T\) 的前缀和后缀且 \(t\) 的 长度为奇数。

\(2\le n\le 10^{6}\)

和上面那题的做法一样。
初值看着设成 1/-1,然后每次自增 2。验证当前情况下如果不满足条件就自减 2,直到减成 - 1。
这题 *2700 不是很认可。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 1e6 + 10, bse = 131, mod = 1e9 + 3579;
int n, ans, f[N], hsh[N], pw[N];
char ch[N];
int get(int l, int r) { return (1ll * hsh[r] - 1ll * hsh[l - 1] * pw[r - l + 1] % mod + mod) % mod; }

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> ch + 1; pw[0] = 1;
    rep(i,1,n) hsh[i] = (1ll * hsh[i - 1] * bse + ch[i]) % mod, pw[i] = 1ll * pw[i - 1] * bse % mod;
    int st = (n >> 1) + (n & 1);
    if (n & 1) f[st] = -1;
    else f[st] = (ch[st] == ch[st + 1] ? 1 : -1); 
    pre(i, st-1, 0) {
        f[i] = f[i + 1] + 2; 
        while (f[i] > -1 and get(i, i + f[i] - 1) != get(n - i + 1 - f[i] + 1, n - i + 1)) f[i] -= 2;
    } rep(i,1,st) cout << f[i] << ' ';
}



CF961E

给定长度为 \(n\) 的序列 \(a\),求满足 \(1\le i<j\le n\land a_i\ge j\land a_j\ge i\) 的二元组 \((i,j)\) 的个数。

\(1\le n\le 2\times 10^5\)\(1\le a_i\le 10^9\)

没错,做完上面那题之后看到的。

考虑怎么维护三个性质。
我们一点点删掉不满足条件的 \(i\),最开始 \(j = n\),所有 \(i\) 都满足条件。
\(a\) 先排个序,然后 j 倒着扫统计。首先把所有满足 \(a_i < j\)\(i\) 都扔掉。然后查询小于等于 \(\min(a_j,j - 1)\)\(i\) 的个数作为答案。

然后做完了。总时间复杂度 \(O(n\log n)\)

*1900 正常。紫色的不是很认可。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 2e5 + 10;
int n, a[N], id[N];
long long ans;

struct BIT {
    int Index[N];
    void add(int p, int v) { for (; p <= n; p += p & -p) Index[p] += v; }
    int qry(int p) { int ret = 0; for (; p; p ^= p & -p) ret += Index[p]; return ret; }
} B;

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n; rep(i,1,n) cin >> a[i], id[i] = i;
    sort(id + 1, id + 1 + n, [&](const int &x, const int &y){ return a[x] < a[y]; });
    int ptr = 1;
    rep(i,1,n) B.Index[i] = (i & -i);
    rep(j,1,n) {
        while (ptr <= n and a[id[ptr]] < j) B.add(id[ptr], -1), ++ ptr;
        ans += B.qry(min(j - 1, a[j])); 
    } cout << ans << '\n';
}



P7728 旧神归来

题面长,不搬。

首先很容易发现一个东西
就是你应该用生成函数去刻画这个东西(
而且我们只需要去刻画这个叶子

\(S(T_i)\) 为树 \(T_i\) 上的叶子组成的多重集对应的生成函数。具体地,

\[S(T_i)= \sum_{i=1}^{\infty}[深度为 \ i \ 的叶子的个数]x^i \]

然后记 \(\text{minf}(f(x))\) 为一个多项式 \(f(x)\) 中最低次项。\(\text{minf}(x^3 + 3x) = x\) 这种样子的。

然后可以刻画了。

\[S(T_{i+1}) = S(T_i) \times \text{minf}(S(T_i)) + S(T_i) - \text{minf}(S(T_i)) = S(T_i) \times(1 + \text{minf}(S(T_i))) - \text{minf}(S(T_i)) \]

挺长的

然后我们观察两次合并会产生些什么效果。
为了不让式子变长,我们记第一次换的叶子是 \(x^d\),第二次换的叶子是 \(x^e\)
那两次换叶子的功效就是

\[(S(T_i) \times(1 + x^d) - x^d)\times (1 + x^e) - x^e =S(T_i)(1 + x^d)(1 + x^e) -x^{d +e} - x^d - x^e = (S(T_i) - 1)(1 + x^d)(1 + x^e) +1 \]

挺好看

这告诉我们其实两次换叶子是等价的,顺序不重要。

然后我们假设深度为 \(i\) 的叶子被换了 \(a_i\) 次后换完。

\[(S(T_0) - 1)\times \prod_{i\ge 1}(1 + x^i)^{a_i} + 1 = 0 \]

然后就是

\[\prod_{i\ge 1}(1 + x^i)^{a_i} = \frac{1}{1 - S(T_0)} \]

看到 \(\prod\) 取对数:

\[\sum_{i\ge 1} a_i \ln (1 + x^i) = -\sum_{i\ge 1} \sum_{j\ge 1} \frac{(-1)^{j}a_i}{j} x^{ij} = \ln \frac{1}{1 - S(T_0)} \]

对比系数得到

\[\sum_{ij = n}\frac{(-1)^{j+1} a_i}{j} = [x^n] \ln \frac{1}{1 - S(T_0)} \]

注意这里是 \(-x^i\)\(j\) 次方,而不是 \(-x\)\(ij\) 次方。因此最后 \(-1\) 的指数是 \(j+1\)

\[a_n = [x^n] \ln \frac{1}{1 - S(T_0)} + \sum_{ij = n \land i \neq n}\frac{(-1)^{j} a_i}{j} \]

做完了。总时间复杂度 \(O(n\log n)\)
还没实现。晚点再说。

upd: 实现了 写的是枚举因子的写法所以是 \(O(n\sqrt n)\)
注意枚举因子时 \(i\times i = n\) 时不要统计两次。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
int n, m, t1, t2, ans[N];
unsigned a[N];
vector<int> g[N];

void dfs(int u, int fa, int dep) {
    if (g[u].size() == 1) a[dep] = (1ll * a[dep] - 1 + mod) % mod;
    for (auto v : g[u]) if (v != fa) dfs(v, u, dep + 1);
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    rep(i,2,n) cin >> t1 >> t2, g[t1].emplace_back(t2), g[t2].emplace_back(t1);
    dfs(1, 0, 0); a[0] ++;
    poly f(a, a + m + 1);
    f = f.ln();
    rep(i,1,m) f[i] = (mod - 1ll * f[i]) % mod;
    rep(k,1,m) {
        ans[k] = f[k];
        for (int i = 1, j; i * i <= k; ++ i) if (k % i == 0) {
            j = k / i;
            if (i != k) ans[k] = (ans[k] + 1ll * ( (j & 1) ? mod - 1 : 1 ) * ans[i] % mod * math::ginv(j)) % mod;
            if (i * i != k) {
                swap(i, j);
                if (i != k) ans[k] = (ans[k] + 1ll * ( (j & 1) ? mod - 1 : 1 ) * ans[i] % mod * math::ginv(j)) % mod;
                swap(i, j);
            }
        }
    } 
    rep(i,1,m) ans[i] = (ans[i - 1] + ans[i]) % mod;
    rep(i,1,m) cout << ans[i] << '\n';
}
posted @ 2022-11-25 17:30  joke3579  阅读(133)  评论(8编辑  收藏  举报